using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MonitorPlatform.Data.Entities;
using System.Text.Json;

namespace MonitorPlatform.Data.Context
{
    public class MonitoringDbContext : DbContext
    {
        public MonitoringDbContext(DbContextOptions<MonitoringDbContext> options)
            : base(options)
        {
        }

        public DbSet<Application> Applications { get; set; }
        public DbSet<PerformanceMetric> PerformanceMetrics { get; set; }
        public DbSet<ErrorLog> ErrorLogs { get; set; }
        public DbSet<ApiMetric> ApiMetrics { get; set; }
        public DbSet<UserBehavior> UserBehaviors { get; set; }
        public DbSet<Session> Sessions { get; set; }
        public DbSet<AlertRule> AlertRules { get; set; }
        public DbSet<AlertHistory> AlertHistories { get; set; }
        public DbSet<MetricAggregate> MetricAggregates { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            base.OnModelCreating(modelBuilder);

            // Configure schemas
            modelBuilder.HasDefaultSchema("monitoring");

            // Configure Application entity
            modelBuilder.Entity<Application>(entity =>
            {
                entity.ToTable("Applications", "config");
                entity.HasKey(e => e.Id);
                entity.HasAlternateKey(e => e.AppId);
                entity.Property(e => e.AppId).HasMaxLength(50);
                entity.Property(e => e.AppName).HasMaxLength(200).IsRequired();
                entity.Property(e => e.ApiKey).HasMaxLength(100).IsRequired();
                entity.HasIndex(e => e.ApiKey);
                entity.HasIndex(e => e.AppId).IsUnique();
            });

            // Configure PerformanceMetric entity
            modelBuilder.Entity<PerformanceMetric>(entity =>
            {
                entity.ToTable("PerformanceMetrics");
                entity.Property(e => e.AppId).HasMaxLength(50).IsRequired();
                entity.Property(e => e.SessionId).HasMaxLength(100).IsRequired();
                entity.Property(e => e.UserId).HasMaxLength(100);
                entity.Property(e => e.Url).HasMaxLength(500);
                entity.Property(e => e.UserAgent).HasMaxLength(500);
                entity.Property(e => e.DeviceType).HasMaxLength(50);
                entity.Property(e => e.Os).HasMaxLength(50);
                entity.Property(e => e.Browser).HasMaxLength(50);
                entity.Property(e => e.BrowserVersion).HasMaxLength(20);
                entity.Property(e => e.ScreenResolution).HasMaxLength(20);
                entity.Property(e => e.ViewportSize).HasMaxLength(20);
                entity.Property(e => e.ConnectionType).HasMaxLength(20);
                entity.Property(e => e.EffectiveType).HasMaxLength(20);
                entity.Property(e => e.Country).HasMaxLength(50);
                entity.Property(e => e.Region).HasMaxLength(50);
                entity.Property(e => e.City).HasMaxLength(50);

                // Configure relationship
                entity.HasOne(e => e.Application)
                    .WithMany(a => a.PerformanceMetrics)
                    .HasForeignKey(e => e.AppId)
                    .HasPrincipalKey(a => a.AppId);

                // Indexes
                entity.HasIndex(e => new { e.AppId, e.Timestamp })
                    .HasDatabaseName("IX_PerformanceMetrics_AppId_Timestamp");
            });

            // Configure ErrorLog entity
            modelBuilder.Entity<ErrorLog>(entity =>
            {
                entity.ToTable("ErrorLogs");
                entity.Property(e => e.AppId).HasMaxLength(50).IsRequired();
                entity.Property(e => e.SessionId).HasMaxLength(100).IsRequired();
                entity.Property(e => e.UserId).HasMaxLength(100);
                entity.Property(e => e.ProcessingId).HasMaxLength(100);
                entity.Property(e => e.ErrorType).HasMaxLength(50).IsRequired();
                entity.Property(e => e.FileName).HasMaxLength(500);
                entity.Property(e => e.Url).HasMaxLength(500);
                entity.Property(e => e.UserAgent).HasMaxLength(500);
                entity.Property(e => e.Referrer).HasMaxLength(500);
                entity.Property(e => e.ApiUrl).HasMaxLength(500);
                entity.Property(e => e.HttpMethod).HasMaxLength(10);
                entity.Property(e => e.ResolvedBy).HasMaxLength(100);

                // Configure relationship
                entity.HasOne(e => e.Application)
                    .WithMany(a => a.ErrorLogs)
                    .HasForeignKey(e => e.AppId)
                    .HasPrincipalKey(a => a.AppId);

                // Indexes
                entity.HasIndex(e => new { e.AppId, e.Timestamp })
                    .HasDatabaseName("IX_ErrorLogs_AppId_Timestamp");
                entity.HasIndex(e => e.ErrorType)
                    .HasDatabaseName("IX_ErrorLogs_ErrorType");
                entity.HasIndex(e => e.SessionId)
                    .HasDatabaseName("IX_ErrorLogs_SessionId");
            });

            // Configure ApiMetric entity
            modelBuilder.Entity<ApiMetric>(entity =>
            {
                entity.ToTable("ApiMetrics");
                entity.Property(e => e.AppId).HasMaxLength(50).IsRequired();
                entity.Property(e => e.SessionId).HasMaxLength(100).IsRequired();
                entity.Property(e => e.UserId).HasMaxLength(100);
                entity.Property(e => e.ApiUrl).HasMaxLength(500).IsRequired();
                entity.Property(e => e.ApiPath).HasMaxLength(500);
                entity.Property(e => e.HttpMethod).HasMaxLength(10).IsRequired();

                // Configure relationship
                entity.HasOne(e => e.Application)
                    .WithMany(a => a.ApiMetrics)
                    .HasForeignKey(e => e.AppId)
                    .HasPrincipalKey(a => a.AppId);

                // Indexes
                entity.HasIndex(e => new { e.AppId, e.Timestamp })
                    .HasDatabaseName("IX_ApiMetrics_AppId_Timestamp");
            });

            // Configure UserBehavior entity
            modelBuilder.Entity<UserBehavior>(entity =>
            {
                entity.ToTable("UserBehaviors");
                entity.Property(e => e.AppId).HasMaxLength(50).IsRequired();
                entity.Property(e => e.SessionId).HasMaxLength(100).IsRequired();
                entity.Property(e => e.UserId).HasMaxLength(100);
                entity.Property(e => e.EventType).HasMaxLength(50).IsRequired();
                entity.Property(e => e.EventName).HasMaxLength(200);
                entity.Property(e => e.TargetElement).HasMaxLength(500);
                entity.Property(e => e.TargetClass).HasMaxLength(200);
                entity.Property(e => e.TargetId).HasMaxLength(200);
                entity.Property(e => e.PageUrl).HasMaxLength(500);
                entity.Property(e => e.PageTitle).HasMaxLength(500);
                entity.Property(e => e.Referrer).HasMaxLength(500);

                // Configure relationship
                entity.HasOne(e => e.Application)
                    .WithMany()
                    .HasForeignKey(e => e.AppId)
                    .HasPrincipalKey(a => a.AppId);

                // Indexes
                entity.HasIndex(e => new { e.AppId, e.Timestamp })
                    .HasDatabaseName("IX_UserBehaviors_AppId_Timestamp");
                entity.HasIndex(e => e.EventType)
                    .HasDatabaseName("IX_UserBehaviors_EventType");
                entity.HasIndex(e => e.SessionId)
                    .HasDatabaseName("IX_UserBehaviors_SessionId");
            });

            // Configure Session entity
            modelBuilder.Entity<Session>(entity =>
            {
                entity.ToTable("Sessions");
                entity.HasKey(e => e.SessionId);
                entity.Property(e => e.SessionId).HasMaxLength(100);
                entity.Property(e => e.AppId).HasMaxLength(50).IsRequired();
                entity.Property(e => e.UserId).HasMaxLength(100);
                entity.Property(e => e.EntryPage).HasMaxLength(500);
                entity.Property(e => e.ExitPage).HasMaxLength(500);
                entity.Property(e => e.Referrer).HasMaxLength(500);
                entity.Property(e => e.UserAgent).HasMaxLength(500);
                entity.Property(e => e.DeviceType).HasMaxLength(50);
                entity.Property(e => e.Os).HasMaxLength(50);
                entity.Property(e => e.Browser).HasMaxLength(50);
                entity.Property(e => e.BrowserVersion).HasMaxLength(20);
                entity.Property(e => e.Ip).HasMaxLength(50);
                entity.Property(e => e.Country).HasMaxLength(50);
                entity.Property(e => e.Region).HasMaxLength(50);
                entity.Property(e => e.City).HasMaxLength(50);

                // Configure relationship
                entity.HasOne(e => e.Application)
                    .WithMany()
                    .HasForeignKey(e => e.AppId)
                    .HasPrincipalKey(a => a.AppId);

                // Indexes
                entity.HasIndex(e => new { e.AppId, e.StartTime })
                    .HasDatabaseName("IX_Sessions_AppId_StartTime");
                entity.HasIndex(e => e.UserId)
                    .HasDatabaseName("IX_Sessions_UserId");
            });

            // Configure AlertRule entity
            modelBuilder.Entity<AlertRule>(entity =>
            {
                entity.ToTable("AlertRules", "config");
                entity.HasKey(e => e.RuleId);
                entity.Property(e => e.AppId).HasMaxLength(50).IsRequired();
                entity.Property(e => e.RuleName).HasMaxLength(200).IsRequired();
                entity.Property(e => e.RuleType).HasMaxLength(50).IsRequired();
                entity.Property(e => e.MetricType).HasMaxLength(50).IsRequired();
                entity.Property(e => e.Severity).HasMaxLength(20).IsRequired();
                entity.Property(e => e.CreatedBy).HasMaxLength(100);

                // Configure relationship
                entity.HasOne(e => e.Application)
                    .WithMany(a => a.AlertRules)
                    .HasForeignKey(e => e.AppId)
                    .HasPrincipalKey(a => a.AppId);
            });

            // Configure AlertHistory entity
            modelBuilder.Entity<AlertHistory>(entity =>
            {
                entity.ToTable("AlertHistory");
                entity.HasKey(e => e.AlertId);
                entity.Property(e => e.AppId).HasMaxLength(50).IsRequired();
                entity.Property(e => e.AlertTitle).HasMaxLength(500);
                entity.Property(e => e.Status).HasMaxLength(20);
                entity.Property(e => e.AcknowledgedBy).HasMaxLength(100);
                entity.Property(e => e.ResolvedBy).HasMaxLength(100);

                // Configure relationship
                entity.HasOne(e => e.AlertRule)
                    .WithMany(r => r.AlertHistories)
                    .HasForeignKey(e => e.RuleId);

                // Indexes
                entity.HasIndex(e => new { e.AppId, e.AlertTime })
                    .HasDatabaseName("IX_AlertHistory_AppId_AlertTime");
            });

            // Configure MetricAggregate entity
            modelBuilder.Entity<MetricAggregate>(entity =>
            {
                entity.ToTable("MetricAggregates");
                entity.Property(e => e.AppId).HasMaxLength(50).IsRequired();
                entity.Property(e => e.MetricType).HasMaxLength(50).IsRequired();
                entity.Property(e => e.AggregateType).HasMaxLength(20).IsRequired();

                // Indexes
                entity.HasIndex(e => new { e.AppId, e.MetricType, e.TimeBucket })
                    .HasDatabaseName("IX_MetricAggregates_Lookup");
            });

            // Configure value conversions for JSON columns
            var jsonOptions = new JsonSerializerOptions
            {
                PropertyNamingPolicy = JsonNamingPolicy.CamelCase
            };

            var stringListConverter = new ValueConverter<List<string>, string>(
                v => JsonSerializer.Serialize(v, jsonOptions),
                v => JsonSerializer.Deserialize<List<string>>(v, jsonOptions) ?? new List<string>()
            );

            var dictionaryConverter = new ValueConverter<Dictionary<string, string>, string>(
                v => JsonSerializer.Serialize(v, jsonOptions),
                v => JsonSerializer.Deserialize<Dictionary<string, string>>(v, jsonOptions) ?? new Dictionary<string, string>()
            );

            // Apply global query filters
            modelBuilder.Entity<Application>()
                .HasQueryFilter(e => e.IsActive);

            // Seed initial data
            SeedData(modelBuilder);
        }

        private void SeedData(ModelBuilder modelBuilder)
        {
            // Seed default application
            modelBuilder.Entity<Application>().HasData(new Application
            {
                Id = 1,
                AppId = "default-app",
                AppName = "Default Application",
                Description = "Default monitoring application",
                ApiKey = "default-api-key-change-in-production",
                IsActive = true,
                CreatedAt = DateTime.UtcNow
            });
        }

        public override int SaveChanges()
        {
            UpdateTimestamps();
            return base.SaveChanges();
        }

        public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
        {
            UpdateTimestamps();
            return base.SaveChangesAsync(cancellationToken);
        }

        private void UpdateTimestamps()
        {
            var entries = ChangeTracker
                .Entries()
                .Where(e => e.Entity is BaseEntity && (e.State == EntityState.Added || e.State == EntityState.Modified));

            foreach (var entry in entries)
            {
                var entity = (BaseEntity)entry.Entity;

                if (entry.State == EntityState.Added)
                {
                    entity.CreatedAt = DateTime.UtcNow;
                }
                else
                {
                    entity.UpdatedAt = DateTime.UtcNow;
                }
            }
        }
    }
} 